/*
    programmefilter.cpp
    (c) 2003 Thor Sigvaldason, Isaac Richards, and ?? ??
    Part of the mythTV project
*/

#include <mythtv/mythcontext.h>
#include <mythtv/uitypes.h>

#include "globals.h"
#include "programmefilter.h"
#include "programmelist.h"
#include "dbaccess.h"
#include "metadata.h"
#include "metadatalistmanager.h"
#include "programmeutils.h"

#include <set>

enum GenreFilter {
    kGenreFilterAll = -1,
    kGenreFilterUnknown = 0
};
/*
enum CategoryFilter {
    kCategoryFilterAll = -1,
    kCategoryFilterUnknown = 0
};

enum BrowseFilter {
    kBrowseFilterAll = -1
};
*/

enum UserRatingFilter {
    kUserRatingFilterAll = -1
};

enum InetRefFilter {
    kInetRefFilterAll = -1,
    kInetRefFilterUnknown = 0
};

enum CoverFileFilter {
    kCoverFileFilterAll = -1,
    kCoverFileFilterNone = 0
};

const unsigned int ProgrammeFilterSettings::FILTER_MASK;
const unsigned int ProgrammeFilterSettings::SORT_MASK;

ProgrammeFilterSettings::ProgrammeFilterSettings(bool loaddefaultsettings,
                                         const QString& _prefix) :
    /*category(kCategoryFilterAll),*/ genre(kGenreFilterAll),
    userrating(kUserRatingFilterAll), /*browse(kBrowseFilterAll),*/
    m_inetref(kInetRefFilterAll), m_coverfile(kCoverFileFilterAll),
    orderby(kOrderByTitle), m_parental_level(ParentalLevel::plNone),
    m_changed_state(0)
{
    if (!_prefix)
        prefix = "ProgrammeDefault";
    else
        prefix = _prefix + "Default";

    // do nothing yet
    if (loaddefaultsettings)
    {
/*
        category = gContext->GetNumSetting(QString("%1Category").arg(prefix), kCategoryFilterAll);
        browse = gContext->GetNumSetting(QString("%1Browse").arg(prefix), kBrowseFilterAll);*/

        genre = gContext->GetNumSetting(QString("%1Genre").arg(prefix),
                                        kGenreFilterAll);
        userrating =
                gContext->GetNumSetting(QString("%1Userrating").arg(prefix),
                                        kUserRatingFilterAll);
        m_inetref = gContext->GetNumSetting(QString("%1InetRef").arg(prefix),
                kInetRefFilterAll);
        m_coverfile = gContext->GetNumSetting(QString("%1CoverFile")
                .arg(prefix), kCoverFileFilterAll);
        orderby = (ordering)gContext->GetNumSetting(QString("%1Orderby")
                                                    .arg(prefix),
                                                    kOrderByTitle);
    }
}

ProgrammeFilterSettings::ProgrammeFilterSettings(const ProgrammeFilterSettings &rhs) :
    m_changed_state(0)
{
    *this = rhs;
}

ProgrammeFilterSettings &
ProgrammeFilterSettings::operator=(const ProgrammeFilterSettings &rhs)
{
    prefix = rhs.prefix;
/*
    if (category != rhs.category)
    {
        m_changed_state |= kFilterCategoryChanged;
        category = rhs.category;
    }

    if (browse != rhs.browse)
    {
        m_changed_state |= kFilterBrowseChanged;
        browse = rhs.browse;
    }*/

    if (genre != rhs.genre)
    {
        m_changed_state |= kFilterGenreChanged;
        genre = rhs.genre;
    }

    if (userrating != rhs.userrating)
    {
        m_changed_state |= kFilterUserRatingChanged;
        userrating = rhs.userrating;
    }

    if (m_inetref != rhs.m_inetref)
    {
        m_changed_state |= kFilterInetRefChanged;
        m_inetref = rhs.m_inetref;
    }

    if (m_coverfile != rhs.m_coverfile)
    {
        m_changed_state |= kFilterCoverFileChanged;
        m_coverfile = rhs.m_coverfile;
    }

    if (orderby != rhs.orderby)
    {
        m_changed_state |= kSortOrderChanged;
        orderby = rhs.orderby;
    }

    if (m_parental_level != rhs.m_parental_level)
    {
        m_changed_state |= kFilterParentalLevelChanged;
        m_parental_level = rhs.m_parental_level;
    }

    return *this;
}

void ProgrammeFilterSettings::saveAsDefault()
{
/*
    gContext->SaveSetting(QString("%1Category").arg(prefix), category);
    gContext->SaveSetting(QString("%1Browse").arg(prefix), browse);*/

    gContext->SaveSetting(QString("%1Genre").arg(prefix), genre);
    gContext->SaveSetting(QString("%1Userrating").arg(prefix), userrating);
    gContext->SaveSetting(QString("%1InetRef").arg(prefix), m_inetref);
    gContext->SaveSetting(QString("%1CoverFile").arg(prefix), m_coverfile);
    gContext->SaveSetting(QString("%1Orderby").arg(prefix), orderby);
}

bool ProgrammeFilterSettings::matches_filter(const Metadata &mdata) const
{
    bool matches = true;

    if (genre != kGenreFilterAll)
    {
        matches = false;

        const Metadata::genre_list &gl = mdata.Genres();
        for (Metadata::genre_list::const_iterator p = gl.begin();
             p != gl.end(); ++p)
        {
            if ((matches = p->first == genre))
            {
                break;
            }
        }
    }
/*
    if (matches && category != kCategoryFilterAll)
    {
        matches = category == mdata.getCategoryID();
    }

    if (matches && browse != kBrowseFilterAll)
    {
        matches = mdata.Browse() == browse;
    }*/

    if (matches && userrating != kUserRatingFilterAll)
    {
        matches = mdata.UserRating() >= userrating;
    }

    if (matches && m_inetref != kInetRefFilterAll)
    {
        matches = mdata.InetRef() == PROGRAMME_INETREF_DEFAULT;
    }

    if (matches && m_coverfile != kCoverFileFilterAll)
    {
        matches = isDefaultCoverFile(mdata.CoverFile());
    }

    if (matches && m_parental_level)
    {
        matches = (mdata.ShowLevel() != ParentalLevel::plNone) &&
                (mdata.ShowLevel() <= m_parental_level);
    }

    return matches;
}

/// Compares two Metadata instances
bool ProgrammeFilterSettings::meta_less_than(const Metadata &lhs,
                                         const Metadata &rhs,
                                         bool sort_ignores_case) const
{
    bool ret = false;
    switch (orderby)
    {
        case kOrderByTitle:
        {
            Metadata::SortKey lhs_key;
            Metadata::SortKey rhs_key;
            if (lhs.hasSortKey() && rhs.hasSortKey())
            {
                lhs_key = lhs.getSortKey();
                rhs_key = rhs.getSortKey();
            }
            else
            {
                lhs_key = Metadata::GenerateDefaultSortKey(lhs,
                                                           sort_ignores_case);
                rhs_key = Metadata::GenerateDefaultSortKey(rhs,
                                                           sort_ignores_case);
            }
            ret = lhs_key < rhs_key;
            break;
        }
        case kOrderByUserRatingDescending:
        {
            ret = lhs.UserRating() > rhs.UserRating();
            break;
        }
        case kOrderByFilename:
        {
            QString lhsfn(sort_ignores_case ?
                          lhs.Filename().lower() : lhs.Filename());
            QString rhsfn(sort_ignores_case ?
                          rhs.Filename().lower() : rhs.Filename());
            ret = QString::localeAwareCompare(lhsfn, rhsfn) < 0;
            break;
        }
        case kOrderByID:
        {
            ret = lhs.ID() < rhs.ID();
            break;
        }
        default:
        {
            VERBOSE(VB_IMPORTANT, QString("Error: unknown sort type %1")
                    .arg(orderby));
        }
    }

    return ret;
}

/////////////////////////////////
// ProgrammeFilterDialog
/////////////////////////////////
ProgrammeFilterDialog::ProgrammeFilterDialog(FilterSettingsProxy *fsp,
                                 MythMainWindow *parent_,
                                 QString window_name,
                                 QString theme_filename,
                                 const ProgrammeList &programme_list,
                                 const char *name_) :
    MythThemedDialog(parent_, window_name, theme_filename, name_),
    /*browse_select(0),*/ orderby_select(0), userrating_select(0),
    /*category_select(0),*/ save_button(0), done_button(0), numprogrammes_text(0),
    m_intetref_select(0), m_coverfile_select(0), m_fsp(fsp),
    m_programme_list(programme_list)
{
    //
    //  The only thing this screen does is let the
    //  user set (some) metadata information. It only
    //  works on a single metadata entry.
    //

    m_settings = m_fsp->getSettings();

    wireUpTheme();
    fillWidgets();
    update_numprogramme();
    assignFirstFocus();
}

void ProgrammeFilterDialog::update_numprogramme()
{
    if (numprogrammes_text)
    {
        int programme_count = m_programme_list.test_filter(m_settings);

        if (programme_count > 0)
        {
            numprogrammes_text->SetText(
                    QString(tr("Result of this filter : %1 programme(s)"))
                    .arg(programme_count));
        }
        else
        {
            numprogrammes_text->SetText(
                    QString(tr("Result of this filter : No Programmes")));
        }
    }
}

void ProgrammeFilterDialog::fillWidgets()
{

    typedef std::set<int> int_list;
    int_list user_ratings;

    const MetadataListManager::metadata_list &mdl =
            m_programme_list.getListCache().getList();
    for (MetadataListManager::metadata_list::const_iterator p = mdl.begin();
         p != mdl.end(); ++p)
    {

        user_ratings.insert(static_cast<int>((*p)->UserRating()));
    }
/*
    if (category_select)
    {
        category_select->addItem(kCategoryFilterAll, QObject::tr("All"));

        const ProgrammeCategory::entry_list &vcl =
                ProgrammeCategory::getCategory().getList();
        for (ProgrammeCategory::entry_list::const_iterator p = vcl.begin();
             p != vcl.end(); ++p)
        {
            category_select->addItem(p->first, p->second);
        }

        category_select->addItem(kCategoryFilterUnknown,
                                 PROGRAMME_CATEGORY_UNKNOWN);
        category_select->setToItem(m_settings.getCategory());
    }

    if (browse_select)
    {
        browse_select->addItem(kBrowseFilterAll, QObject::tr("All"));
        browse_select->addItem(1, QObject::tr("Yes"));
        browse_select->addItem(0, QObject::tr("No"));
        browse_select->setToItem(m_settings.getBrowse());
    }*/

    if (genre_select)
    {
        genre_select->addItem(kGenreFilterAll, QObject::tr("All"));

        const ProgrammeGenre::entry_list &gl = ProgrammeGenre::getGenre().getList();
        for (ProgrammeGenre::entry_list::const_iterator p = gl.begin();
             p != gl.end(); ++p)
        {
            genre_select->addItem(p->first, p->second);
        }

        genre_select->addItem(kGenreFilterUnknown, PROGRAMME_GENRE_UNKNOWN);
        genre_select->setToItem(m_settings.getGenre());
    }

    if (userrating_select)
    {
        userrating_select->addItem(kUserRatingFilterAll, QObject::tr("All"));

        for (int_list::const_reverse_iterator p = user_ratings.rbegin();
             p != user_ratings.rend(); ++p)
        {
            userrating_select->addItem(*p, QString(">= %1")
                                       .arg(QString::number(*p)));
        }

        userrating_select->setToItem(m_settings.getUserrating());
    }

    if (m_intetref_select)
    {
        m_intetref_select->addItem(kInetRefFilterAll, QObject::tr("All"));
        m_intetref_select->addItem(kInetRefFilterUnknown,
                QObject::tr("Unknown"));
        m_intetref_select->setToItem(m_settings.getInteRef());
    }

    if (m_coverfile_select)
    {
        m_coverfile_select->addItem(kCoverFileFilterAll, QObject::tr("All"));
        m_coverfile_select->addItem(kCoverFileFilterNone,
                QObject::tr("None"));
        m_coverfile_select->setToItem(m_settings.getCoverFile());
    }

    if (orderby_select)
    {
        orderby_select->addItem(ProgrammeFilterSettings::kOrderByTitle,
                                QObject::tr("Title"));
        orderby_select->addItem(
                ProgrammeFilterSettings::kOrderByUserRatingDescending,
                QObject::tr("User Rating"));
        orderby_select->addItem(ProgrammeFilterSettings::kOrderByFilename,
                                QObject::tr("Filename"));
        orderby_select->addItem(ProgrammeFilterSettings::kOrderByID,
                                QObject::tr("Programme ID"));
        orderby_select->setToItem(m_settings.getOrderby());
    }
}

namespace
{
    void widget_testset(UISelectorType *&ret, UIType *current,
            UISelectorType *sel)
    {
        if (sel && current == sel) ret = sel;
    }
}

void ProgrammeFilterDialog::keyPressEvent(QKeyEvent *e)
{
    bool handled = false;
    bool something_pushed = false;

    QStringList actions;
    gContext->GetMainWindow()->TranslateKeyPress("Programme", e, actions);

    for (unsigned int i = 0; i < actions.size() && !handled; i++)
    {
        QString action = actions[i];
        handled = true;

        if (action == "UP")
            nextPrevWidgetFocus(false);
        else if (action == "DOWN")
            nextPrevWidgetFocus(true);
        else if ((action == "LEFT") || (action == "RIGHT"))
        {
            something_pushed = false;

            UISelectorType *currentSelector = NULL;
            UIType *focused = getCurrentFocusWidget();
/*
            widget_testset(currentSelector, focused, category_select);
            widget_testset(currentSelector, focused, browse_select);*/

            widget_testset(currentSelector, focused, genre_select);
            widget_testset(currentSelector, focused, userrating_select);
            widget_testset(currentSelector, focused, m_intetref_select);
            widget_testset(currentSelector, focused, m_coverfile_select);
            widget_testset(currentSelector, focused, orderby_select);

            if (currentSelector)
            {
                currentSelector->push(action == "RIGHT");
                something_pushed = true;
            }

            if (!something_pushed)
            {
                activateCurrent();
            }
        }
        else if (action == "SELECT")
            activateCurrent();
        else if (action == "0")
        {
            if (done_button)
                done_button->push();
        }
        else
            handled = false;
    }

    if (!handled)
        MythThemedDialog::keyPressEvent(e);
}

void ProgrammeFilterDialog::takeFocusAwayFromEditor(bool up_or_down)
{
    nextPrevWidgetFocus(up_or_down);

    MythRemoteLineEdit *which_editor = (MythRemoteLineEdit *)sender();

    if (which_editor)
    {
        which_editor->clearFocus();
    }
}

void ProgrammeFilterDialog::saveAsDefault()
{
     m_settings.saveAsDefault();
     this->saveAndExit();
}

void ProgrammeFilterDialog::saveAndExit()
{
    m_fsp->setSettings(m_settings);
    reject();
}

void ProgrammeFilterDialog::setUserRating(int new_userrating)
{
        m_settings.setUserrating(new_userrating);
        update_numprogramme();
}

void ProgrammeFilterDialog::setGenre(int new_genre)
{
        m_settings.setGenre(new_genre);
        update_numprogramme();
}
/*
void ProgrammeFilterDialog::setCategory(int new_category)
{
        m_settings.setCategory(new_category);
        update_numprogramme();
}

void ProgrammeFilterDialog::setBrowse(int new_browse)
{
        m_settings.setBrowse(new_browse);
        update_numprogramme();
}*/

void ProgrammeFilterDialog::setInetRef(int new_inetref)
{
    m_settings.setInetRef(new_inetref);
    update_numprogramme();
}

void ProgrammeFilterDialog::setCoverFile(int new_coverfile)
{
    m_settings.setCoverFile(new_coverfile);
    update_numprogramme();
}

void ProgrammeFilterDialog::setOrderby(int new_orderby)
{
        m_settings.setOrderby(
                (ProgrammeFilterSettings::ordering)new_orderby);
        update_numprogramme();
}

void ProgrammeFilterDialog::wireUpTheme()
{
    userrating_select = getUISelectorType("userrating_select");
    if (userrating_select)
        connect(userrating_select, SIGNAL(pushed(int)),
                this, SLOT(setUserRating(int)));
/*
    category_select = getUISelectorType("category_select");
    if (category_select)
        connect(category_select, SIGNAL(pushed(int)),
                this, SLOT(setCategory(int)));

    browse_select = getUISelectorType("browse_select");
    if (browse_select)
        connect(browse_select, SIGNAL(pushed(int)),
                this, SLOT(setBrowse(int)));*/

    genre_select = getUISelectorType("genre_select");
    if (genre_select)
        connect(genre_select,SIGNAL(pushed(int)),
                this, SLOT(setGenre(int)));

    m_intetref_select = getUISelectorType("inetref_select");
    if (m_intetref_select)
        connect(m_intetref_select, SIGNAL(pushed(int)),
                this, SLOT(setInetRef(int)));

    m_coverfile_select = getUISelectorType("coverfile_select");
    if (m_coverfile_select)
        connect(m_coverfile_select, SIGNAL(pushed(int)),
                this, SLOT(setCoverFile(int)));

    orderby_select = getUISelectorType("orderby_select");
    if (orderby_select)
        connect(orderby_select, SIGNAL(pushed(int)),
                this, SLOT(setOrderby(int)));

    save_button = getUITextButtonType("save_button");

    if (save_button)
    {
        save_button->setText(tr("Save as default"));
        connect(save_button, SIGNAL(pushed()), this, SLOT(saveAsDefault()));
    }

    done_button = getUITextButtonType("done_button");
    if (done_button)
    {
        done_button->setText(tr("Done"));
        connect(done_button, SIGNAL(pushed()), this, SLOT(saveAndExit()));
    }

    numprogrammes_text = getUITextType("numprogrammes_text");
    buildFocusList();
}

ProgrammeFilterDialog::~ProgrammeFilterDialog()
{
}
